class_name GameplayAbilityManager
extends RefCounted
## 能力管理器，用于管理对象能力的授予、移除、激活、失活、应用、结束、更新
## 能力管理器保存着对象所持有的能力，当能力对目标应用时，则会保存应用的能力

## 能力管理器的持有者
var owner: Variant

## 能力缓存
## { ability_name-String: [ ability-GameplayAbility, abilty_spec-GameplayAbilitySpec ] }
var ability_map: Dictionary


func _init(init_owner: Variant) -> void:
	owner = init_owner


func grant_ability(ability_asset: GameplayAbilityAsset) -> void:
	if not owner.tag_manager.has_all_tags(ability_asset.tags_all_required) or\
	   owner.tag_manager.has_any_tag(ability_asset.tags_cancelled_ability):
		Logger.debug(
			"Ability '%s' is unsuccessfully granted with tags '%s'." % 
			[ability_asset.name, ability_asset.tags_cancelled_ability])
		return
	
	# TODO: 重复授予能力
	if ability_map.has(ability_asset.name):
		Logger.debug("Grant the same ability with given ability asset '%s'." % ability_asset.name)
		return
	
	ability_map[ability_asset.name] = [ability_asset.create_spec(owner), null]


func grant_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		grant_ability(ability_asset)


func remove_ability(ability_asset: GameplayAbilityAsset) -> void:
	var ability_cache = ability_map.get(ability_asset.name)
	if not ability_cache:
		Logger.warning("No such ability with given ability asset '%s'." % ability_asset.name)
		return
	
	# INFO:
	# 理论上来说可以在 GameplayAbility 中添加 end_ability 接口来调用 GameplayAbilitySpec 中的 end_ability 来结束应用的能力。
	# 但是在重载 GameplayAbilitySpec 时要求手动调用其中的 end_ability，因此上述的调用方法就会有问题。
	# 因此最终选择将各个模块拆开：
	# 1. GameplayAbilityManager 集中管理生命周期。
	# 2. GameplayAbility 只管理自身能力的授予。
	# 3. GameplayAbilitySpec 只管理自身能力应用时的效果处理，并在调用 end_ability 时额外设置 expired 为 true，自身不处理生命周期。
	# 因此，在移除能力时，首先需要结束所有应用的能力，随后删除授予的能力。
	ability_cache[1].end_ability()
	ability_map.erase(ability_asset.name)


func remove_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		remove_ability(ability_asset)


func activate_ability(ability_asset: GameplayAbilityAsset) -> void:
	var ability_cache = ability_map.get(ability_asset.name)
	if not ability_cache:
		Logger.warning("No such ability with given ability asset '%s'." % ability_asset.name)
		return
	ability_cache[1].activate()


func activate_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		activate_ability(ability_asset)


func deactivate_ability(ability_asset: GameplayAbilityAsset) -> void:
	var ability_cache = ability_map.get(ability_asset.name)
	if not ability_cache:
		Logger.warning("No such ability with given ability asset '%s'." % ability_asset.name)
		return
	ability_cache[1].deactivate()


func deactivate_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		deactivate_ability(ability_asset)


func try_apply_ability(ability_asset: GameplayAbilityAsset, target: Variant) -> void:
	if owner.tag_manager.has_any_tag(ability_asset.tags_cancelled_ability):
		return

	var ability_cache = ability_map.get(ability_asset.name)
	if not ability_cache:
		Logger.warning("No such ability with given ability asset '%s'." % ability_asset.name)
		return

	if not ability_cache[0].can_apply():
		return
	
	# TODO: 处理堆叠
	if ability_cache[1] != null:
		Logger.warning("Ability '%s' has been already applied to taget '%s'." % [ability_cache[0].ability_asset.name, target.name])
		return
	
	ability_cache[1] = ability_cache[0].create_spec()
	ability_cache[1].apply()


func try_apply_abilities(ability_assets: Array[GameplayAbilityAsset], target: Variant) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		try_apply_ability(ability_asset, target)


func try_apply_ability_by_name(ability_name: String, target: Variant) -> void:
	if ability_map.has(ability_name):
		try_apply_ability(ability_map[ability_name][0].ability_asset, target)
	else:
		Logger.warning("No such ability with given name '%s'." % ability_name)


func end_ability(ability_asset: GameplayAbilityAsset, target: Variant) -> void:
	var ability_cache = ability_map.get(ability_asset.name)
	if not ability_cache:
		Logger.warning("No such ability with given ability asset '%s'." % ability_asset.name)
		return

	if ability_cache[1] != null:
		ability_cache[1].end_ability()
		ability_cache[1] = null
	else:
		Logger.warning("No such ability '%s' has been applied to target '%s'." % [ability_asset.name, target.name])


func on_update(delta: float) -> void:
	for ability_cache in ability_map.values():
		ability_cache[0].on_update(delta)
		
		if ability_cache[1]:
			if ability_cache[1].expired:
				# GameplayAbilitySpec 过期时要么是由自己调用 end_ability，要么是在 GameplayAbilityManager 调用 end_ability（此时会直接清除缓存），
				# 不管使用哪种方法，都会调用 GameplayAbilitySpec 的 end_ability，
				# 故此处直接清除缓存即可。
				ability_cache[1] = null
			else:
				ability_cache[1].on_update(delta)
